package net.m2technologies.open_arm.transport;

import net.m2technologies.open_arm.transport.tranreport.TransactionReportMediator;
import net.m2technologies.open_arm.transport.transaction.TransactionMediator;
import net.m2technologies.open_arm.utilities.collections.CollectionUtils;
import net.m2technologies.open_arm.utilities.collections.ExceptionalPredicate;
import net.m2technologies.open_arm.utilities.collections.ExceptionalTransformer;
import org.apache.commons.pool.BasePoolableObjectFactory;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.opengroup.arm40.tranreport.ArmTranReport;
import org.opengroup.arm40.transaction.ArmTransaction;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.*;

/**
 * Copyright 2005 Mark Masterson<br> <br> Licensed under the Apache License, Version 2.0 (the "License");<br> you may
 * not use this file except in compliance with the License.<br> You may obtain a copy of the License at<br> <br>
 * http://www.apache.org/licenses/LICENSE-2.0<br> <br> Unless required by applicable law or agreed to in writing,
 * software<br> distributed under the License is distributed on an "AS IS" BASIS,<br> WITHOUT WARRANTIES OR CONDITIONS
 * OF ANY KIND, either express or implied.<br> See the License for the specific language governing permissions and<br>
 * limitations under the License.<br>
 * <p/>
 * <p>Description: </p>
 *
 * @author Mark Masterson
 * @version 0.010
 */
public class TransportMediatorBrokerImpl implements TransportMediatorBroker {

    private static final int INITIAL_POOL_SIZE = 6;

    private final GenericObjectPool transportMediators;

    public TransportMediatorBrokerImpl(final String fileName) {
        this(new GenericObjectPool(new TransportMediatorPoolFactory(obtainImplementationNames(fileName), null)));
    }

    public TransportMediatorBrokerImpl(final Map mediatorConfigurations) {
        this(new GenericObjectPool(new TransportMediatorPoolFactory(obtainImplementationNames(mediatorConfigurations),
                                                                    mediatorConfigurations)));
    }

    private TransportMediatorBrokerImpl(final GenericObjectPool transportMediators) {
        this.transportMediators = transportMediators;
        initializeMediatorPool();
    }

    public synchronized void releaseTransportMediators(final Collection mediators) {
        try {
            this.transportMediators.returnObject(mediators);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public synchronized Collection getTransactionTransportMediators(final ArmTransaction transactionDelegate) {
        Collection result;
        try {
            result = CollectionUtils.select((Collection) this.transportMediators.borrowObject(),
                                            new ExceptionalPredicate() {
                                                protected boolean basicEvaluate(final Object o)
                                                        throws Exception {
                                                    if (null == o) return false;
                                                    return o instanceof TransactionMediator;
                                                }
                                            });
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        result = fillTransportMediators(result, transactionDelegate);
        return result;
    }

    public synchronized Collection getTransactionReportTransportMediators(final ArmTranReport transactionDelegate) {
        Collection result;
        try {
            result = CollectionUtils.select((Collection) this.transportMediators.borrowObject(),
                                            new ExceptionalPredicate() {
                                                protected boolean basicEvaluate(final Object o)
                                                        throws Exception {
                                                    if (null == o) return false;
                                                    return o instanceof TransactionReportMediator;
                                                }
                                            });
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        result = fillTransportMediators(result, transactionDelegate);
        return result;
    }

    private static final synchronized Collection obtainImplementationNames(final String fileName) {
        final Collection result;
        if (null != fileName) {
            result = getImplementationNamesFromFile(fileName);
        } else {
            result = getDefaultImplementationNames();
        }
        return result;
    }

    private static final synchronized Collection obtainImplementationNames(final Map configurations) {
        final Collection result;
        if (null != configurations && !configurations.isEmpty()) {
            result = configurations.keySet();
        } else {
            result = getDefaultImplementationNames();
        }
        return result;
    }

    private static final synchronized Collection getImplementationNamesFromFile(final String fileName) {
        final Collection result = new ArrayList();
        try {
            final BufferedReader inputFile = new BufferedReader(new FileReader(fileName));
            String lineOfText;
            while (null != (lineOfText = inputFile.readLine())) {
                result.add(lineOfText);
            }
        } catch (IOException e) {
            throw new RuntimeException(new StringBuffer().append("Error loading-reading the configuration file!  ")
                    .append("Tried to load the file with the name:  ")
                    .append(fileName)
                    .append(", Original exception").toString(), e);
        }
        return result;
    }

    private static final synchronized Collection getDefaultImplementationNames() {
        final Collection result = new ArrayList();
        result.add("net.m2technologies.open_arm.transport.transaction.logging.LoggingMediator");
        return result;
    }

    private final void initializeMediatorPool() {
        setMediatorPoolToUnlimitedGrowth();
        try {
            // prep the pool with a half dozen Sets of Mediators
            final Collection tmpList = getInitialLoadFromTransportMediatorPool();
            for (Iterator tmpListIt = tmpList.iterator(); tmpListIt.hasNext();) {
                this.transportMediators.returnObject(tmpListIt.next());
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private Collection getInitialLoadFromTransportMediatorPool() throws Exception {
        final Collection tmpList = new ArrayList();
        for (int i = 0; INITIAL_POOL_SIZE > i; i++) {
            tmpList.add(this.transportMediators.borrowObject());
        }
        return tmpList;
    }

    private void setMediatorPoolToUnlimitedGrowth() {
        this.transportMediators.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_GROW);
        this.transportMediators.setMaxActive(-1);
    }

    private final Collection fillTransportMediators(final Collection result, final Object transactionDelegate) {
        Collection result1 = result;
        result1 = CollectionUtils.collect(result1,
                                          new ExceptionalTransformer() {
                                              protected Object basicTransform(final Object o) throws Exception {
                                                  ((TransportMediator) o).setTransactionalDelegate(transactionDelegate);
                                                  return o;
                                              }
                                          });
        return result1;
    }

    private static final class TransportMediatorPoolFactory extends BasePoolableObjectFactory {

        private final Collection registeredImplementationNames;
        private final Map mediatorConfigurations;

        TransportMediatorPoolFactory(final Collection registeredImplementationNames, final Map mediatorConfigurations) {
            this.registeredImplementationNames = registeredImplementationNames;
            this.mediatorConfigurations = mediatorConfigurations;
        }

        public Object makeObject() throws Exception {
            final Set result = new HashSet();
            result.addAll(CollectionUtils.collect(this.registeredImplementationNames, new ExceptionalTransformer() {
                protected Object basicTransform(final Object o)
                        throws Exception {
                    return instantiateImplementationFromClassName((String) o,
                                                                  null);
                }
            }));
            return result;
        }

        private TransportMediator instantiateImplementationFromClassName(final String className,
                                                                         final Object transactionDelegate)
                throws ClassNotFoundException,
                       NoSuchMethodException,
                       IllegalAccessException,
                       InvocationTargetException,
                       InstantiationException {
            try {
                final Class clazz = Class.forName(className);
                final Constructor ctor = clazz.getConstructor(new Class[]{Object.class});
                final TransportMediator result = (TransportMediator) ctor.newInstance(new Object[]{transactionDelegate});
                if (null != this.mediatorConfigurations && !this.mediatorConfigurations.isEmpty()) {
                    final Object configuration = this.mediatorConfigurations.get(className);
                    result.setConfiguration(configuration);
                }
                return result;
            } catch (NoSuchMethodException e) {
                throw e;
            } catch (SecurityException e) {
                throw e;
            } catch (InstantiationException e) {
                throw e;
            } catch (IllegalAccessException e) {
                throw e;
            } catch (IllegalArgumentException e) {
                throw e;
            } catch (InvocationTargetException e) {
                throw e;
            }
        }
    }
}
